#include <boost/graph/adjacency_list.hpp>

#include <iostream>
#include <string>
#include <map>
#include <algorithm>

#include "GraphTypes.h"

struct NodeProperty
{
  long term_id;
	long term_frequency;
};

template <typename StringType = std::string>
struct EdgeProperty
{
  int section_type;
	StringType connecting_word;
	typedef StringType type;
};

typedef EdgeProperty<> EdgePropertyString;

template <typename Graph,
          typename TermIdMap, 
					typename TermFrequencyMap, 
					typename EdgeSectionTypeMap,
					typename EdgeConnectingWordMap
         >
void print_graph(const Graph &graph,
								 TermIdMap term_id_map,
								 TermFrequencyMap term_frequency_map,
								 EdgeSectionTypeMap edge_section_type_map,
								 EdgeConnectingWordMap edge_connecting_word_map
								 );

using namespace boost;

		//edge connecting word property
			typedef boost::adjacency_list<boost::listS,
																	boost::listS,
																	boost::directedS,
																	NodeProperty,
																	EdgePropertyString 
			> graph_t;



	typedef boost::graph_traits<graph_t>::vertex_descriptor vertex_t;
	typedef boost::graph_traits<graph_t>::edge_descriptor edge_t;
	typedef boost::graph_traits<graph_t>::vertices_size_type size_t;
	typedef boost::graph_traits<graph_t>::out_edge_iterator out_edge_iter_t;
	typedef boost::graph_traits<graph_t>::vertex_iterator vertex_iter_t;

	typedef std::map<long,vertex_t> vertex_map_t;

	template <typename VertexType, typename Map>
	typename VertexType * get_vertex(long id, Map & map);

	typedef boost::vertex_bundle_type<graph_t>::type node_bundle_t;
	typedef boost::edge_bundle_type<graph_t>::type edge_bundle_t;

	typedef boost::property_map<graph_t, long NodeProperty::* > :: type term_id_property_t;
	typedef term_id_property_t term_frequency_property_t;

	typedef boost::property_map<graph_t, int EdgePropertyString::* > :: type edge_section_type_property_t;
	typedef boost::property_map<graph_t, EdgePropertyString::type EdgePropertyString::* > :: type edge_connecting_word_property_t;

	typedef boost::property_traits<edge_section_type_property_t>::value_type edge_section_value_type;


	template <typename TermIdProperty>
	struct NodeSort {
		bool operator()(vertex_t &vertex1,vertex_t &vertex2) {
			return get(TermIdProperty,vertex1) < get(TermIdProperty,vertex2);
		}
	
	};

int main()
{
	
	
  vertex_map_t vertex_map;
	
	graph_t graph;//size

	vertex_t vertex ;

	term_id_property_t term_id = get(&NodeProperty::term_id,graph);
	term_frequency_property_t term_frequency = get(&NodeProperty::term_frequency,graph);
  edge_section_type_property_t edge_section_type = get(&EdgePropertyString::section_type,graph);
	edge_connecting_word_property_t edge_connecting_word = get(&EdgePropertyString::connecting_word,graph);

	//std::cout << get(term_id,vertex) << std::endl;
	//std::cout << get(term_frequency,vertex) << std::endl;

	int i=0;

	//fill in the graph

	vertex = add_vertex(graph);
	vertex_map.insert(vertex_map_t::value_type(10,vertex));
	put(&NodeProperty::term_id,graph,vertex,10);
	put(&NodeProperty::term_frequency,graph,vertex,1);

	vertex = add_vertex(graph);
	vertex_map.insert(vertex_map_t::value_type(20,vertex));
	put(&NodeProperty::term_id,graph,vertex,20);
	put(&NodeProperty::term_frequency,graph,vertex,4);

	vertex = add_vertex(graph);
	vertex_map.insert(vertex_map_t::value_type(30,vertex));
	put(&NodeProperty::term_id,graph,vertex,30);
	put(&NodeProperty::term_frequency,graph,vertex,6);

	vertex = add_vertex(graph);
	vertex_map.insert(vertex_map_t::value_type(40,vertex));
	put(&NodeProperty::term_id,graph,vertex,40);
	put(&NodeProperty::term_frequency,graph,vertex,13);

	vertex = add_vertex(graph);
	vertex_map.insert(vertex_map_t::value_type(50,vertex));
	put(&NodeProperty::term_id,graph,vertex,50);
  put(&NodeProperty::term_frequency,graph,vertex,7);

	vertex_t *ver1,*ver2;
	edge_t edge;
	bool inserted;

	if ((ver1 = get_vertex<vertex_t,vertex_map_t>(10,vertex_map))) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver1,graph);
		put(&EdgePropertyString::section_type,graph,edge,999);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string("slava,kisilevich"));
	}

	if ((ver1 = get_vertex<vertex_t,vertex_map_t>(10,vertex_map))) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver1,graph);
		put(&EdgePropertyString::section_type,graph,edge,11111);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string("marina,litvak"));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(10,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(20,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(&EdgePropertyString::section_type,graph,edge,888);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string("puki,kuki"));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(10,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(30,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(&EdgePropertyString::section_type,graph,edge,777);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string(""));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(30,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(20,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(&EdgePropertyString::section_type,graph,edge,666);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string("business"));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(30,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(40,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(&EdgePropertyString::section_type,graph,edge,555);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string("pandemonium"));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(20,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(40,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(&EdgePropertyString::section_type,graph,edge,444);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string("something,nothing,on,the"));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(20,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(50,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(&EdgePropertyString::section_type,graph,edge,333);
		put(&EdgePropertyString::connecting_word,graph,edge,std::string("kuki"));
	}

	//print the graph
	print_graph(graph,term_id,term_frequency,edge_section_type,edge_connecting_word);
}

template <typename Graph,
          typename TermIdMap, 
					typename TermFrequencyMap, 
					typename EdgeSectionTypeMap,
					typename EdgeConnectingWordMap
         >
void print_graph(const Graph &graph,
								 TermIdMap term_id_map,
								 TermFrequencyMap term_frequency_map,
								 EdgeSectionTypeMap edge_section_type_map,
								 EdgeConnectingWordMap edge_connecting_word_map
								 )
{
  

   vertex_iter_t vertex_i, vertex_end;
	 tie(vertex_i,vertex_end) = vertices(graph);

	 out_edge_iter_t out_edge_i, out_edge_end;
	 for (; vertex_i != vertex_end; ++vertex_i) //iterate through vertices
	 {
		 std::cout << "id : " << term_id_map[*vertex_i] << std::endl;
		 std::cout << "term_frequency_value : " << get(term_frequency_map,*vertex_i) << std::endl;

     for (tie(out_edge_i,out_edge_end) = out_edges(*vertex_i,graph); out_edge_i != out_edge_end; ++out_edge_i) //get all out edges of the vertex
		 {
			 vertex_t trgt = target(*out_edge_i,graph);

			 std::cout << "        connected to id : " << get(term_id_map,trgt) << std::endl;
		   std::cout << "        term_frequency_value : " << get(term_frequency_map,trgt) << std::endl;

			 edge_section_value_type edge_section_type = get(edge_section_type_map,*out_edge_i);

			 std::cout << "        section type : " << edge_section_type << std::endl;
		   std::cout << "        connecting word  : " << get(edge_connecting_word_map,*out_edge_i) << std::endl;


			 std::cout << "\n";
		 }

		 std::cout << "\n";
	 }
}

template <typename VertexType, typename Map>
typename VertexType * get_vertex(long id, Map & map)
{
	Map::iterator iter = map.find(id);
	if (iter == map.end()) return NULL;
	return &(iter->second);
}